package com.qjl.parser.logicflow;

import com.qjl.core.ELNode;
import com.qjl.parser.ELParser;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author : zhangrongyan
 * @date : 2023/3/3 16:51
 */
@Data
@AllArgsConstructor
public class LogicFlowParser implements ELParser {

    private LogicFlow logicFlow;

    @Override
    public ELNode extractElNode() {

        List<LfNode> nodes = logicFlow.getNodes();
        List<LfEdge> edges = logicFlow.getEdges();
        if (CollectionUtils.isEmpty(nodes) || CollectionUtils.isEmpty(edges)) {
            return ELNode.initThenNode();
        }
        //根节点
        String rootId = extractInitialNode(edges);
        //sourceId-> List<EdgeEntity>
        Map<String, List<LfEdge>> sourceNodeToEdgesMap = edges.stream().collect(Collectors.groupingBy(LfEdge::getSourceNodeId));
        //id -> NodeEntity
        Map<String, LfNode> nodeMap = nodes.stream().collect(Collectors.toMap(LfNode::getId, Function.identity()));

        //获取when节点
        Map<String, LfNode> whenNodeMap = nodes.stream()
                .filter(e -> ELNode.ELNameEnum.WHEN.name().equals(e.getProperties().get(LfNodePropertyEnum.nodeType.name())))
                .collect(Collectors.toMap(LfNode::getId, Function.identity()));

        //先准备好各个并行组的树节点
        //gourpId -> List<ELNode> 一个when中有多棵树
        List<WhenGroupTrees> whenGroupTrees = doWhenGroup(whenNodeMap, nodeMap, sourceNodeToEdgesMap);

        ELNode elNode = ELNode.initThenNode();
        elNode.setChild(doExtract(rootId, sourceNodeToEdgesMap, nodeMap, whenNodeMap, whenGroupTrees));
        return elNode;
    }

    /**
     * 获取下游节点ids
     *
     * @param nodeIds              节点id
     * @param sourceNodeToEdgesMap 源节点来边缘Map
     * @return {@link Set}<{@link String}>
     */
    private Set<String> getTargetIds(List<String> nodeIds, Map<String, List<LfEdge>> sourceNodeToEdgesMap) {
        return nodeIds.stream().map(e ->
                Optional.ofNullable(sourceNodeToEdgesMap.get(e))
                        .orElse(new ArrayList<>())
                        .stream().map(LfEdge::getTargetNodeId).collect(Collectors.toSet()))
                .map(e -> e.toArray(new String[0]))
                .flatMap(Arrays::stream)
                .collect(Collectors.toSet());
    }

    /**
     * 先单独分析whenGroup中多棵树
     *
     * @param whenNodeMap          当节点Map
     * @param nodeMap              节点Map
     * @param sourceNodeToEdgesMap 源节点来边缘Map
     * @return {@link List}<{@link WhenGroupTrees}>
     */
    private List<WhenGroupTrees> doWhenGroup(Map<String, LfNode> whenNodeMap, Map<String, LfNode> nodeMap
            , Map<String, List<LfEdge>> sourceNodeToEdgesMap) {
        Collection<LfNode> whenNodeEntities = whenNodeMap.values();
        //所有group的child
        //所有group节点id
        Set<String> allWhenNodeIds = whenNodeEntities.stream().map(LfNode::getId).collect(Collectors.toSet());

        List<WhenGroupTrees> list = new ArrayList<>();
        for (LfNode whenLfNode : whenNodeEntities) {

            WhenGroupTrees whenGroupTrees = new WhenGroupTrees();
            whenGroupTrees.setWhenGroupNodeId(whenLfNode.getId());

            List<String> children = whenLfNode.getChildren();
            //去除其他whenNodeId
            //剩下就是本When中所有的节点
            children.removeAll(allWhenNodeIds);
            ArrayList<String> allNodeExcludedWhenIds = new ArrayList<>(children);
            ArrayList<String> allNodeExcludedWhen = new ArrayList<>(children);

            //获取他们targetIds下游节点
            Set<String> targetIds = getTargetIds(allNodeExcludedWhen, sourceNodeToEdgesMap);
            //去除下游节点
            allNodeExcludedWhen.removeAll(targetIds);
            //剩下就是when中的root节点
            ArrayList<String> allRootNodeInWhenGroup = new ArrayList<>(allNodeExcludedWhen);

            List<ELNode> rootThenELNodes = new ArrayList<>();
            for (String rootId : allRootNodeInWhenGroup) {
                ELNode elNode = ELNode.initThenNode();
                elNode.setChild(doExtractWhen(rootId, sourceNodeToEdgesMap, nodeMap, allNodeExcludedWhenIds));
                rootThenELNodes.add(elNode);
            }
            whenGroupTrees.setElNodeList(rootThenELNodes);

            list.add(whenGroupTrees);
        }
        return list;
    }

    private List<ELNode> doExtractWhen(String currentId,
                                       Map<String, List<LfEdge>> sourceNodeToEdgesMap,
                                       Map<String, LfNode> nodeMap,
                                       List<String> allNodeExcludedWhen) {
        List<ELNode> listNodes = new ArrayList<>();
        LfNode currentLfNode = nodeMap.get(currentId);
        if (currentLfNode == null) {
            return listNodes;
        }

        //连续处理多个只有一个子节点的节点
        while (sourceNodeToEdgesMap.containsKey(currentId)
                && allNodeExcludedWhen.contains(currentId)
                && sourceNodeToEdgesMap.get(currentId).size() == 1) {

            LfNode node = nodeMap.get(currentId);
            listNodes.add(initElNode(node));
            List<LfEdge> edgeEntities = sourceNodeToEdgesMap.get(currentId);
            if (!CollectionUtils.isEmpty(edgeEntities)) {
                //有子节点
                currentId = edgeEntities.get(0).getTargetNodeId();
            }
        }

        if (!allNodeExcludedWhen.contains(currentId)) {
            //非本when中的节点不处理
            return listNodes;
        }
        //叶子节点处理：
        if (!sourceNodeToEdgesMap.containsKey(currentId)) {
            listNodes.add(initElNode(nodeMap.get(currentId)));
            return listNodes;
        }

        // 子节点数大于1个的处理
        //这种节点可能是IF/ SWITCH / WHEN
        LfNode lfNode1 = nodeMap.get(currentId);
        List<String> strings = Arrays.asList(ELNode.ELNameEnum.IF.name(), ELNode.ELNameEnum.SWITCH.name());
        String propertyNodeType = getLfNodeProperty(lfNode1, LfNodePropertyEnum.nodeType.name());
        if (strings.contains(propertyNodeType)) {
            ELNode curNode = initElNode(lfNode1);
            List<ELNode> children = new ArrayList<>();
            curNode.setChild(children);
            listNodes.add(curNode);
            List<LfEdge> edgeEntities = sourceNodeToEdgesMap.get(currentId);
            for (LfEdge edge : edgeEntities) {
                String targetNodeId = edge.getTargetNodeId();
                LfNode lfNode = nodeMap.get(targetNodeId);
                String nodeAliasId = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeAliasId.name());
                ELNode thenNode = ELNode.initThenNode();
                thenNode.setAliasNodeId(nodeAliasId);
                thenNode.setChild(doExtractWhen(targetNodeId, sourceNodeToEdgesMap, nodeMap, allNodeExcludedWhen));
                children.add(thenNode);
            }
        }

        return listNodes;
    }


    private List<ELNode> doExtract(String currentId,
                                   Map<String, List<LfEdge>> sourceNodeToEdgesMap,
                                   Map<String, LfNode> nodeMap,
                                   Map<String, LfNode> whenNodeMap,
                                   List<WhenGroupTrees> whenGroupTrees
    ) {
        List<ELNode> listNodes = new ArrayList<>();
        LfNode currentLfNode = nodeMap.get(currentId);
        if (currentLfNode == null) {
            return listNodes;
        }


        //连续处理多个只有一个子节点的节点
        while (sourceNodeToEdgesMap.containsKey(currentId) && sourceNodeToEdgesMap.get(currentId).size() == 1) {
            LfNode node = nodeMap.get(currentId);
            listNodes.add(initElNode(node));
            List<LfEdge> edgeEntities = sourceNodeToEdgesMap.get(currentId);
            if (!CollectionUtils.isEmpty(edgeEntities)) {
                //有子节点
                currentId = edgeEntities.get(0).getTargetNodeId();
            }
        }

        //叶子节点处理：
        if (!sourceNodeToEdgesMap.containsKey(currentId)) {
            listNodes.add(initElNode(nodeMap.get(currentId)));
            return listNodes;
        }

        // 子节点数大于1个的处理
        //这种节点可能是IF/ SWITCH / WHEN
        LfNode lfNode1 = nodeMap.get(currentId);
        List<String> strings = Arrays.asList(ELNode.ELNameEnum.IF.name(), ELNode.ELNameEnum.SWITCH.name());
        String propertyNodeType = getLfNodeProperty(lfNode1, LfNodePropertyEnum.nodeType.name());
        if (strings.contains(propertyNodeType)) {
            ELNode curNode = initElNode(lfNode1);
            List<ELNode> children = new ArrayList<>();
            curNode.setChild(children);
            listNodes.add(curNode);
            List<LfEdge> edgeEntities = sourceNodeToEdgesMap.get(currentId);
            for (LfEdge edge : edgeEntities) {
                String targetNodeId = edge.getTargetNodeId();
                LfNode lfNode = nodeMap.get(targetNodeId);
                String nodeAliasId = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeAliasId.name());
                ELNode thenNode = ELNode.initThenNode();
                thenNode.setAliasNodeId(nodeAliasId);
                thenNode.setChild(doExtract(targetNodeId, sourceNodeToEdgesMap, nodeMap, whenNodeMap, whenGroupTrees));
                children.add(thenNode);
            }
            return listNodes;
        }


        ELNode curNode = initElNode(lfNode1);
        listNodes.add(curNode);


        //当前节点的所有下游节点
        Set<String> targetNodeIds = sourceNodeToEdgesMap.get(currentId).stream()
                .map(LfEdge::getTargetNodeId)
                .collect(Collectors.toSet());

        //发现他们都在某一个whenGroup的chilIds中
        Optional<LfNode> first = whenNodeMap.values().stream()
                .filter(e -> e.getChildren().containsAll(targetNodeIds)).findFirst();

        if (first.isPresent()) {
            Map<String, WhenGroupTrees> whenGroupResultMap = whenGroupTrees.stream()
                    .collect(Collectors.toMap(WhenGroupTrees::getWhenGroupNodeId, Function.identity()));

            String whenGroupId = first.get().getId();
            WhenGroupTrees whenGroupTree = whenGroupResultMap.get(whenGroupId);
            //elNode[0] -> 树
            Map<String, ELNode> elNodeMap = whenGroupTree.getElNodeList().stream()
                    .filter(e -> !e.getChild().isEmpty())
                    .collect(Collectors.toMap(e -> e.getChild().get(0).getNodeId(), Function.identity()));


            ELNode whenNode = ELNode.initWhenNode();
            //分别找对应的group
            for (String rootIdInWhen : targetNodeIds) {
                LfNode lfNode = nodeMap.get(rootIdInWhen);
                String nodeId = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeId.name());
                ELNode elNode = elNodeMap.get(nodeId);
                if (elNode == null) {

                    //本树的下节点是另外一个when的叉数, 把末尾节点清除，使用末尾节点doExtract
                    //返回when开始节点和when数组
                    List<ELNode> elNodes = doExtract(rootIdInWhen, sourceNodeToEdgesMap, nodeMap, whenNodeMap, whenGroupTrees);

                    ELNode thenNode = ELNode.initThenNode();
                    thenNode.setChild(elNodes);
                    whenNode.addChild(thenNode);
                } else {
                    //本树的下节点是外部,记录到函数外,继续doExtract
                    //本数的下节点是内部另外一棵树
                    whenNode.addChild(elNode);
                }
            }
            listNodes.add(whenNode);
        }

        //获取尾巴节点，分别继续接着doExtract
        //所有when的childIds
        Set<String> whenChildIds = whenNodeMap.values().stream()
                .map(e -> e.getChildren().toArray(new String[0]))
                .flatMap(Arrays::stream)
                .collect(Collectors.toSet());

        if (!whenChildIds.contains(currentId)) {
            String externalNodeIdOutSideWhen = doFindOutSideWhenNode(currentId, sourceNodeToEdgesMap, whenChildIds);
            List<ELNode> elNodes = doExtract(externalNodeIdOutSideWhen, sourceNodeToEdgesMap, nodeMap, whenNodeMap, whenGroupTrees);
            if (!CollectionUtils.isEmpty(elNodes)) {
                ELNode thenNode = ELNode.initThenNode();
                thenNode.setChild(elNodes);
                listNodes.add(thenNode);
            }
        }

        return listNodes;
    }


    /**
     * 找when的外部的第一个节点
     *
     * @param enterWhenSourceId    当输入源id
     * @param sourceNodeToEdgesMap 源节点来边缘Map
     * @param whenChildIds         when中所有孩子节点
     * @return {@link String}
     */
    private String doFindOutSideWhenNode(String enterWhenSourceId,
                                         Map<String, List<LfEdge>> sourceNodeToEdgesMap,
                                         Set<String> whenChildIds) {
        List<LfEdge> edgeEntities = sourceNodeToEdgesMap.get(enterWhenSourceId);
        //没边
        if (CollectionUtils.isEmpty(edgeEntities)) {
            return null;
        }
        for (LfEdge edge : edgeEntities) {
            String targetNodeId = edge.getTargetNodeId();
            //不在框框中，就返回
            if (!whenChildIds.contains(targetNodeId)) {
                return targetNodeId;
            } else {
                String s = doFindOutSideWhenNode(targetNodeId, sourceNodeToEdgesMap, whenChildIds);
                if (s == null) {
                    continue;
                } else {
                    return s;
                }
            }
        }
        return null;
    }

    /**
     * init el节点
     *
     * @param lfNode Lf节点
     * @return {@link ELNode}
     */
    private ELNode initElNode(LfNode lfNode) {
        if (lfNode == null) {
            return null;
        }
        String nodeId = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeId.name());
        String nodeTag = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeTag.name());
        String nodeData = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeData.name());
        String nodeType = getLfNodeProperty(lfNode, LfNodePropertyEnum.nodeType.name());
        String conditionNodeId = getLfNodeProperty(lfNode, LfNodePropertyEnum.conditionNodeId.name());

        ELNode node = new ELNode();
        node.setNodeId(nodeId);
        node.setTag(nodeTag);
        node.setData(nodeData);
        node.setName(nodeType);
        node.setConditionNodeId(conditionNodeId);
        if (ELNode.ELNameEnum.COMMON.name().equals(nodeType)) {
            node.setType(ELNode.ELTypeEnum.idType.name());
        } else {
            node.setType(ELNode.ELTypeEnum.elType.name());
        }
        return node;
    }


    /**
     * 获取Lf节点属性
     *
     * @param nodeMap      节点Map
     * @param propertyName 属性名字
     * @return {@link String}
     */
    private String getLfNodeProperty(LfNode nodeMap, String propertyName) {
        return Optional.ofNullable(nodeMap)
                .map(LfNode::getProperties)
                .map(e -> e.get(propertyName))
                .map(Object::toString)
                .orElse("");
    }


    /**
     * 提取Lf节点的根节点id
     *
     * @param edges 边缘
     * @return {@link String}
     */
    private String extractInitialNode(List<LfEdge> edges) {

        //获取初始化节点
        Set<String> targetNodeIds = edges.stream().map(LfEdge::getTargetNodeId).collect(Collectors.toSet());
        Set<String> sourceNodeIds = edges.stream().map(LfEdge::getSourceNodeId).collect(Collectors.toSet());
        if (sourceNodeIds.size() - targetNodeIds.size() > 1) {
            throw new RuntimeException("只能有一个开始节点");
        }
        for (String sourceNodeId : sourceNodeIds) {
            if (!targetNodeIds.contains(sourceNodeId)) {
                return sourceNodeId;
            }
        }
        return null;
    }


}
